// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_EXTERNAL_REFERENCE_TABLE_H_
#define V8_EXTERNAL_REFERENCE_TABLE_H_

#include <vector>

#include "src/accessors.h"
#include "src/builtins/builtins.h"
#include "src/counters-definitions.h"
#include "src/external-reference.h"

namespace v8 {
namespace internal {

    class Isolate;

    // ExternalReferenceTable is a helper class that defines the relationship
    // between external references and their encodings. It is used to build
    // hashmaps in ExternalReferenceEncoder and ExternalReferenceDecoder.
    class ExternalReferenceTable {
    public:
        // For the nullptr ref, see the constructor.
        static constexpr int kSpecialReferenceCount = 1;
        static constexpr int kExternalReferenceCount = ExternalReference::kExternalReferenceCount;
        static constexpr int kBuiltinsReferenceCount =
#define COUNT_C_BUILTIN(...) +1
            BUILTIN_LIST_C(COUNT_C_BUILTIN);
#undef COUNT_C_BUILTIN
        static constexpr int kRuntimeReferenceCount = Runtime::kNumFunctions - Runtime::kNumInlineFunctions; // Don't count dupe kInline... functions.
        static constexpr int kIsolateAddressReferenceCount = kIsolateAddressCount;
        static constexpr int kAccessorReferenceCount = Accessors::kAccessorInfoCount + Accessors::kAccessorSetterCount;
        // The number of stub cache external references, see AddStubCache.
        static constexpr int kStubCacheReferenceCount = 12;
        static constexpr int kStatsCountersReferenceCount =
#define SC(...) +1
            STATS_COUNTER_NATIVE_CODE_LIST(SC);
#undef SC
        static constexpr int kSize = kSpecialReferenceCount + kExternalReferenceCount + kBuiltinsReferenceCount + kRuntimeReferenceCount + kIsolateAddressReferenceCount + kAccessorReferenceCount + kStubCacheReferenceCount + kStatsCountersReferenceCount;
        static constexpr uint32_t kEntrySize = static_cast<uint32_t>(kSystemPointerSize);
        static constexpr uint32_t kSizeInBytes = kSize * kEntrySize + 2 * kUInt32Size;

        Address address(uint32_t i) const { return ref_addr_[i]; }
        const char* name(uint32_t i) const { return ref_name_[i]; }

        bool is_initialized() const { return is_initialized_ != 0; }

        static const char* ResolveSymbol(void* address);

        static constexpr uint32_t OffsetOfEntry(uint32_t i)
        {
            // Used in CodeAssembler::LookupExternalReference.
            return i * kEntrySize;
        }

        const char* NameFromOffset(uint32_t offset)
        {
            DCHECK_EQ(offset % kEntrySize, 0);
            DCHECK_LT(offset, kSizeInBytes);
            int index = offset / kEntrySize;
            return name(index);
        }

        ExternalReferenceTable() = default;
        void Init(Isolate* isolate);

    private:
        void Add(Address address, int* index);

        void AddReferences(Isolate* isolate, int* index);
        void AddBuiltins(int* index);
        void AddRuntimeFunctions(int* index);
        void AddIsolateAddresses(Isolate* isolate, int* index);
        void AddAccessors(int* index);
        void AddStubCache(Isolate* isolate, int* index);

        Address GetStatsCounterAddress(StatsCounter* counter);
        void AddNativeCodeStatsCounters(Isolate* isolate, int* index);

        STATIC_ASSERT(sizeof(Address) == kEntrySize);
        Address ref_addr_[kSize];
        static const char* const ref_name_[kSize];

        // Not bool to guarantee deterministic size.
        uint32_t is_initialized_ = 0;

        // Redirect disabled stats counters to this field. This is done to make sure
        // we can have a snapshot that includes native counters even when the embedder
        // isn't collecting them.
        // This field is uint32_t since the MacroAssembler and CodeStubAssembler
        // accesses this field as a uint32_t.
        uint32_t dummy_stats_counter_ = 0;

        DISALLOW_COPY_AND_ASSIGN(ExternalReferenceTable);
    };

    STATIC_ASSERT(ExternalReferenceTable::kSizeInBytes == sizeof(ExternalReferenceTable));

} // namespace internal
} // namespace v8
#endif // V8_EXTERNAL_REFERENCE_TABLE_H_
